package org.xteam.sled.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.xteam.parser.runtime.Span;
import org.xteam.sled.ast.EqOp;
import org.xteam.sled.ast.Relop;
import org.xteam.sled.semantic.Field;
import org.xteam.sled.semantic.IDenotable;
import org.xteam.sled.semantic.IErrorContext;
import org.xteam.sled.semantic.IEvaluationContext;
import org.xteam.sled.semantic.RangeConstraintsMaker;
import org.xteam.sled.semantic.exp.Exp;
import org.xteam.sled.semantic.exp.ExpConst;
import org.xteam.sled.semantic.exp.ExpRewriter;

public class LabelledPattern extends BasePattern<LabelledDisjunct> implements IDenotable {
	
	public LabelledPattern(String name) {
		super(name);
	}
	
	public LabelledPattern(LabelledDisjunct disjunct) {
		super(null, disjunct);
	}
	
	public LabelledPattern(String name, LabelledDisjunct disjunct) {
		super(name, disjunct);
	}
	
	public LabelledPattern(String name, List<LabelledDisjunct> disjuncts) {
		super(name, disjuncts);
	}

	public LabelledPattern() {
		super(null);
	}

	public LabelledPattern(LabelledPattern res) {
		super(null, res.disjuncts);
	}

	public AbsoluteProtoPattern anf() {
		// p = this
		// name = name
		// ds = disjuncts
		List<AbsoluteProtoDisjunct> d = new ArrayList<AbsoluteProtoDisjunct>();
		for (LabelledDisjunct l : disjuncts) {
			d.add(l.anf());
		}
		return new AbsoluteProtoPattern(name, d);
	}
	
	public LabelledPattern or(LabelledPattern other) {
		return (LabelledPattern) super.or(other);
	}
	
	public LabelledPattern and(LabelledPattern other) {
		return (LabelledPattern) super.and(other);
	}
	
	public LabelledPattern seq(LabelledPattern other) {
		return (LabelledPattern) super.seq(other);
	}
	
	public LabelledPattern preDots() {
		return (LabelledPattern) super.preDots();
	}
	
	public LabelledPattern postDots() {
		return (LabelledPattern) super.postDots();
	}
	
	public LabelledPattern subst(ExpRewriter subs) {
		return (LabelledPattern) super.subst(subs);
	}
	
	public LabelledPattern nameUnnamed(String newName) {
		return (LabelledPattern) super.nameUnnamed(newName);
	}

	@Override
	protected BasePattern<LabelledDisjunct> createPattern(String name) {
		return new LabelledPattern(name);
	}

	@Override
	public Exp projectInt(IEvaluationContext context, Span span) {
		throw new RuntimeException("not implemented");
	}

	@Override
	public BasePattern projectPattern(IEvaluationContext context, Span span) {
		return this;
	}

	public LatentPattern makeLabelsPCRelative() {
		List<LatentDisjunct> latentDisjuncts = new ArrayList<LatentDisjunct>();
		for (IDisjunct dis : disjuncts) {
			latentDisjuncts.add(((LabelledDisjunct)dis).makeLabelsPCRelative());
		}
		return new LatentPattern(name, latentDisjuncts);
	}

	public LabelledPattern actualizeLatents(boolean actualsOk) {
		List<LabelledDisjunct> n = new ArrayList<LabelledDisjunct>();
		for (LabelledDisjunct d : disjuncts) {
			n.add(d.actualizeLatent(actualsOk));
		}
		return new LabelledPattern(name, n);
	}
	
	public static LabelledPattern constraint (IErrorContext context, Span span, Field field, Relop relop, Exp intExp) {
		if (intExp.isConst()) {
			return staticConstraint(context, span, field, relop,
					((ExpConst) intExp).value());
		}
		return dynamicConstraint(context, field, relop, intExp);
	}

	private static LabelledPattern staticConstraint(IErrorContext context, Span span, Field field, Relop relop, int value) {
		LabelledPattern result = new LabelledPattern();
		for (RangeConstraint<Field> c : new RangeConstraintsMaker<Field>(context, span, field, value).make(relop)) {
			result.add(new LabelledDisjunct(new LabelledSequence(org.xteam.sled.model.Constraints.newConstraint(c))));
		}
		return result;
	}

	private static LabelledPattern dynamicConstraint(IErrorContext context, Field field, Relop relop, Exp intExp) {
		if (! (relop instanceof EqOp)) {
			context.reportError(relop.span(),
					"can only write " + field.name() + " " + relop + " e when e is an integer literal");
			throw new RuntimeException("bad op in dynamic exp");
		}
		Map<Field, Exp> exprs = new HashMap<Field, Exp>();
		exprs.put(field, intExp);
		return new LabelledPattern(new LabelledDisjunct(new LabelledSequence(org.xteam.sled.model.Constraints.newConstraint(field.cls(), exprs))));
	}

}
